package com.spica.platform.redis.page;

import com.spica.platform.redis.repository.RedisRepository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class AbstractPageCacheService<T> implements PageCacheService<T>{
    @Resource
    private RedisRepository redisRepository;

    @Override
    public boolean isEntitiesEmptyForList(long userId) {
        if(getDatesForList(userId).size() == 0){
            return true;
        }
        return false;
    }

    @Override
    public boolean isEntitiesEmptyForListByDate(long userId, String date){
        List<Object> list = getDatesForList(userId);
        return list.contains(date);
    }

    @Override
    public List<Object> getDatesForList(long userId){
        String listKey = this.getListKey(userId);
        return redisRepository.opsForList().range(listKey,0,-1);
    }

    @Override
    public void insertDateForListByDate(long userId, String date){
        String listKey = this.getListKey(userId);
        redisRepository.opsForList().rightPush(listKey, date);
    }

    @Override
    public void setDateExpireForListByDate(long userId, Integer minutes){
        String listKey = getListKey(userId);
        redisRepository.expire(listKey, minutes*60);
    }

    @Override
    public void setEntityExpireForSetByDate(long userId, String date, Integer minutes){
        String setKey = getSetKey(date, userId);
        redisRepository.expire(setKey, minutes*60);
    }

    @Override
    public void setPageExpireForHashByPage(String page, Integer minutes){
        redisRepository.expire(page, minutes*60);
    }
    @Override
    public List<Object> getDateForList(long userId){
        String listKey = getListKey(userId);
        return redisRepository.opsForList().range(listKey,0,-1);
    }

    @Override
    public List<T> getEntitiesForSetByDate(String date, long userId) {
        String setKey = getSetKey(date, userId);
        Set<Object> entitySet = redisRepository.opsForSet().members(setKey);
        return entitySet.stream().map(entityObj -> (T)entityObj).collect(Collectors.toList());
    }

    @Override
    public PageData<T> insertDateForHashByPage(long userId, PageData<T> pageData, String date, List<T> entities) {

        //当前数据集合中，未被当前页面排进去的记录条数
        int leftOverSize;
        //当前数据集合中，已经遍历到的数据下标
        int indexBegin = pageData.getIndexEnd();
        int indexEnd;
        //indexBegin == -1, 上一个集合遍历完毕，leftOverSize为新的问题的size，
        // 此时需要判断leftOverSize + count是否超页，不超页，直接加；若超页，还需要修改leftOverSize
        if (indexBegin == -1) {
            leftOverSize = entities.size();
        } else {
            //indexBegin != -1表示本集合还有元素未遍历完，由于之前遍历过，size需要减去之前的元素个数才能变成leftOverSize
            leftOverSize = entities.size() - indexBegin - 1;
        }

        int temp = (pageData.getPageSize() * pageData.getPageNo());
        //加上该日期下的剩下的所有问题，未超页，count为本页目前的所有记录数量
        String hashKey = getHashKey(pageData.getPageNo(), userId);
        if (pageData.getCount() + leftOverSize <= temp) {
            pageData.setCount(pageData.getCount() + leftOverSize);
            //如果上一次遍历完（indexBegin == -1），则hv为 0 :-1，
            if (indexBegin == -1) {
                redisRepository.opsForHash().put(hashKey, date, 0 + ":-1");
            } else {
                // 如果上一次未遍历完indexBegin ！= -1，则hv还是 (indexBegin + 1): -1
                //你想，本集合上一次本集合没有塞完所有数据，是不是说明上一页已经塞满数据了，所以本集合现在剩下的数据应该塞往下一页了
                redisRepository.opsForHash().put(hashKey, date, (indexBegin + 1) + ":-1");
            }

            pageData.setPageNo(pageData.getPageNo());
            //因为本集合已经全部塞到了本页中，所以集合结束下标为-1
            pageData.setIndexEnd(-1);

            //如果刚好满页，则将pageNo设置为下一页
            if (pageData.getCount() == temp) {
                pageData.setPageNo(pageData.getPageNo() + 1);
            }
            return pageData;
        } else {
            /**
             * 超页,能塞多少塞多少，hv为indexBegin: indexEnd, 新的indexEnd = indexBegin + min（pageSize ，leftOverSize）
             * 并且需要返回新的页号，表示下一数据集合或者还未塞完的本数据集合肯定得塞到下一页去，和该集合的数据下标，方便下次塞数据
             */
            //上一个数据集合遍历完毕，且这个数据集合若全部加入会超页，需要修改leftOverSize
            if (indexBegin == -1) {
                indexBegin = 0;
                leftOverSize = pageData.getPageSize() * pageData.getPageNo() - pageData.getCount();
            } else {
                indexBegin = indexBegin + 1;
            }
            //全部加进去如果超页，则能加多少就加多少，所以是min
            indexEnd = indexBegin + Math.min(pageData.getPageSize() - 1, leftOverSize - 1);
            redisRepository.opsForHash().put(hashKey, date, indexBegin + ":" + indexEnd);
            //超页了，那肯定下次塞数据肯定是塞到下一页去了
            pageData.setPageNo(pageData.getPageNo() + 1);
            pageData.setIndexEnd(indexEnd);
            //超页了，则当前页数据量肯定就是本页的数量
            pageData.setCount(temp);
            return pageData;
        }
    }

    @Override
    public List<T> getEntitiesByPage(long userId, int pageNum) {
        List<T> entities = new ArrayList<>();
        //从Hash中得到当前页的所有日期数据
        Set dates = redisRepository.opsForHash().keys(getHashKey(pageNum, userId));

        for(Object date : dates) {
            String countAndIndex = (String) redisRepository.opsForHash().get(getHashKey(pageNum, userId), date);
            Integer indexBegin = Integer.valueOf(countAndIndex.split(":")[0]);
            Integer indexEnd = Integer.valueOf(countAndIndex.split(":")[1]);


            //通过日期获取问题set
            List<T> entityList = getEntitiesForSetByDate((String) date, userId);

            int index = 0;
            //从indexBegin开始，indexEnd结束读取问题
            for (T entity : entityList) {
                if (indexEnd != -1) {
                    if (indexBegin <= index && index <= indexEnd) {
                        entities.add(entity);
                    } else if (index > indexEnd) {
                        break;
                    }
                } else {
                    //indexEnd == -1  加至最后
                    if (indexBegin <= index) {
                        entities.add(entity);
                    }
                }
                index++;
            }
        }
        return entities;
    }

    /**
     * 往日期-entity的set集合里面插入数据
     * @param userId 用户id
     * @param date 日期
     * @param entity 博客信息
     */
    public void insertEntityForSetByDate(long userId, String date, T entity){
        String setKey = getSetKey(date, userId);
        redisRepository.opsForSet().add(setKey, entity);
    }


    @Override
    public void removeEntityForSetByKey(String setKey, T entity) {
        redisRepository.opsForSet().remove(setKey, entity);
    }

    @Override
    public void addEntityForSetByKey(String setKey, T entity) {
        redisRepository.opsForSet().add(setKey, entity);
    }
}
